import os
import logging
from typing import List, Callable, Dict
from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.types import ASGIApp, Receive, Scope, Send

from app.services.jwt_service import jwt_service

logger = logging.getLogger(__name__)

class JWTMiddleware(BaseHTTPMiddleware):
    """
    JWT验证中间件
    用于校验来自Node服务的JWT令牌
    """
    
    def __init__(
        self, 
        app: ASGIApp,
        excluded_paths: List[str] = None,
    ):
        """
        初始化JWT中间件
        :param app: ASGI应用
        :param excluded_paths: 不需要JWT验证的路径列表
        """
        super().__init__(app)
        # 默认排除的路径
        self.excluded_paths = excluded_paths or [
            "/docs", 
            "/redoc", 
            "/openapi.json",
            "/api/auth/login",
            "/api/auth/register",
            "/api/health"
        ]
        logger.info(f"JWT中间件初始化，排除路径: {self.excluded_paths}")
    
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        """
        处理请求并进行JWT验证
        :param request: FastAPI请求对象
        :param call_next: 下一个处理函数
        :return: 响应对象
        """
        # 检查是否需要跳过验证
        if self._should_skip_auth(request.url.path):
            logger.debug(f"跳过JWT验证: {request.url.path}")
            return await call_next(request)
        
        try:
            # 验证JWT并提取用户信息
            user_data = await jwt_service.verify_request(request)
            # 将用户数据添加到请求状态中，以便后续处理器使用
            request.state.user = user_data
            # 继续处理请求
            return await call_next(request)
            
        except Exception as e:
            # 如果是预检请求(OPTIONS)，则允许通过
            if request.method == "OPTIONS":
                return await call_next(request)
            
            # 记录错误并继续处理请求（路由处理器将根据请求状态决定是否允许访问）
            logger.warning(f"JWT验证失败: {str(e)}")
            # 注意：这里我们让请求继续，但不设置request.state.user
            # API路由处理器可以检查request.state.user是否存在来决定是否允许访问
            return await call_next(request)
    
    def _should_skip_auth(self, path: str) -> bool:
        """
        检查请求路径是否应该跳过认证
        :param path: 请求路径
        :return: 如果应该跳过认证返回True，否则返回False
        """
        # 检查路径是否在排除列表中
        for excluded_path in self.excluded_paths:
            # 完全匹配
            if path == excluded_path:
                return True
            # 前缀匹配（如/docs/开头的所有路径）
            if excluded_path.endswith("*") and path.startswith(excluded_path[:-1]):
                return True
        
        return False 